package com.rf.richfitwheel.utils;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.rf.richfitwheel.common.exception.BusinessException;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.stereotype.Component;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class EasyExcelUtil<T> {

    private List<Map<Integer, String>> dataList = new ArrayList<>();
    private List<T> dataEntityList = new ArrayList<>();

    /**
     * 根据文件路径读取Excel
     * @param filePath 文件路径
     * @param headRowNum 读取行数 0代表从Excel的第一行开始读取
     * @return List<Map<Integer, String>>
     */
    public List<Map<Integer, String>> readExcelByPath(String filePath, Integer headRowNum) {
        ExcelDataListener listener = new ExcelDataListener();
        EasyExcel.read(filePath).sheet().registerReadListener(listener).headRowNumber(headRowNum).doRead();
        return dataList;
    }

    /**
     * 根据文件对象读取Excel
     * @param multipartFile Excel文件
     * @param headRowNum 读取行数 0代表从Excel的第一行开始读取
     * @return List<Map<Integer, String>>
     */
    public List<Map<Integer, String>> readExcelByFile(MultipartFile multipartFile, Integer headRowNum) {
        this.isExcelFile(multipartFile);
        InputStream inputStream = this.getInputStream(multipartFile);
        ExcelDataListener listener = new ExcelDataListener();
        EasyExcel.read(inputStream).sheet().registerReadListener(listener).headRowNumber(headRowNum).doRead();
        return dataList;
    }

    /**
     * 根据文件路径读取Excel,实体类映射
     * 实体类需要使用注解 @ExcelProperty("对应表格列名")
     *                 @ExcelIgnore     忽略字段
     * @param filePath 文件路径
     * @param headRowNum 读取行数 0代表从Excel的第一行开始读取
     * @param t 对应的实体类
     * @return List<T>
     */
    public List<T> excelReadFilePathByEntity(String filePath, Integer headRowNum, Class<T> t) {
        ExcelDataEntityListener listener = new ExcelDataEntityListener();
        EasyExcel.read(filePath, t, listener).sheet().headRowNumber(headRowNum).doRead();
        return dataEntityList;
    }

    /**
     * 根据文件对象读取Excel
     * @param multipartFile Excel文件
     * @param headRowNum 读取行数 0代表从Excel的第一行开始读取
     * @return List<T>
     */
    public List<T> excelReadFileByEntity(MultipartFile multipartFile, Integer headRowNum, T t) {
        this.isExcelFile(multipartFile);
        InputStream inputStream = this.getInputStream(multipartFile);
        ExcelDataEntityListener listener = new ExcelDataEntityListener();
        EasyExcel.read(inputStream, (Class) t, listener).sheet().headRowNumber(headRowNum).doRead();
        return dataEntityList;
    }


    //处理对应实体类监听
    class ExcelDataEntityListener extends AnalysisEventListener<T> {

        @Override
        public void invoke(T t, AnalysisContext analysisContext) {
            dataEntityList.add(t);
        }

        @Override
        public void doAfterAllAnalysed(AnalysisContext analysisContext) {
            dataEntityList.forEach(System.out :: println);
        }
    }

    //无实体类监听
    class ExcelDataListener extends AnalysisEventListener<Map<Integer, String>> {

        @Override
        public void invoke(Map<Integer, String> rowData, AnalysisContext analysisContext) {
            //处理读取数据的每一行数据，rowData是包含一行Excel数据的map集合
            dataList.add(rowData);
        }

        @Override
        public void doAfterAllAnalysed(AnalysisContext analysisContext) {
//            dataList.forEach(map -> map.forEach((index, value) -> {
//                System.out.println(index + "===>" + value);
//            }));
        }
    }

    /**
     * 导出Excel，并下载到浏览器
     * 入参第一个List为行， 0代表第一行，第二个List为每一列数据
     * 需要保证标题与数据列对应，防止数据错行
     * @param response  响应流
     * @param fileName  文件名称
     * @param sheetName sheet页名称
     * @param headList    标题行  Stream.of("姓名", "性别", "身高", "年龄", "班级").collect(Collectors.toList());
     * @param data      数据
     * @throws IOException
     */
    public void writeFileToBrowser(HttpServletResponse response, String fileName, String sheetName,
                     List<String> headList, List<List<String>> data) {
        List<List<String>> header = this.handleHeader(headList);
        response.setHeader("Content-Transfer-Encoding", "binary");
        response.setHeader("Cache-Control", "must-revalidate,post-check=0,pre-check=0");
        response.setHeader("Pragma", "public");
        response.setHeader("Content-Type", "application/vnd.ms-excel");
        try {
            fileName = URLEncoder.encode(fileName, "UTF-8");
            response.setHeader("Content-Disposition", "attachment;filename*=utf-8'zh_cn'" + fileName + ExcelTypeEnum.XLSX.getValue());
            EasyExcel.write(response.getOutputStream())
                    .head(header)
                    .autoCloseStream(Boolean.FALSE)
                    .sheet(sheetName)
                    .doWrite(data);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 导出Excel，并下载到浏览器
     * 需要有对应实体类
     * @param response  响应流
     * @param fName  文件名称
     * @param header    标题行
     * @param data      数据
     * @throws IOException
     */
    public void writeFileToBrowserByEntity(HttpServletResponse response, String fName,
                       Class<T> header, List<T> data) {
        String fileName = StringUtils.isBlank(fName) ? "模板文件" : fName;
        response.setHeader("Content-Transfer-Encoding", "binary");
        response.setHeader("Cache-Control", "must-revalidate,post-check=0,pre-check=0");
        response.setHeader("Pragma", "public");
        response.setHeader("Content-Type", "application/vnd.ms-excel");
        try {
            fileName = URLEncoder.encode(fileName, "UTF-8");
            response.setHeader("Content-Disposition", "attachment;filename*=utf-8'zh_cn'" + fileName + ExcelTypeEnum.XLSX.getValue());
            EasyExcel.write(response.getOutputStream(), header)
                    .head((Class) header)
                    .autoCloseStream(Boolean.FALSE)
                    .sheet("sheet")
                    .doWrite(data);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private List<List<String>> handleHeader(List<String> headList) {
        List<List<String>> header = new ArrayList<>();
        headList.forEach(v -> {
            List<String> columns = new ArrayList<>();
            columns.add(v);
            header.add(columns);
        });
        return header;
    }

    public InputStream getInputStream(MultipartFile multipartFile) {
        InputStream inputStream = null;
        try {
            inputStream = multipartFile.getInputStream();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return inputStream;
    }

    public File convertFile(MultipartFile multipartFile)  {
        File file = new File(multipartFile.getOriginalFilename());
        try {
            file.createNewFile();
            FileCopyUtils.copy(multipartFile.getBytes(), file);
//            InputStream in = multipartFile.getInputStream();
//            OutputStream outputStream = new FileOutputStream(file);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return file;
    }

    public String getFileName(MultipartFile file) {
        String originalFilename = file.getOriginalFilename();
        String name = originalFilename.split("\\.")[0];
        String suffix = originalFilename.split("\\.")[1];
        return name;
    }

    public boolean isExcelFile(MultipartFile file) {
        if (file.isEmpty()) {
            throw new BusinessException("请上传Excel文件！！！");
        }
        try {
            WorkbookFactory.create(file.getInputStream());
            return true;
        } catch (IOException e) {
            throw new BusinessException("文件不是Excel格式！！！");
        }
    }

}


